#include "inc/metroline.hpp"
#include <boost/exception/diagnostic_information.hpp>
#include <boost/program_options.hpp>
#include <boost/program_options/value_semantic.hpp>
#include <exception>
#include <stdexcept>

/* 可选颜色
 * B02020 红
 * 228B22 绿
 */

/**
 * @brief 根据RGB字符串生成颜色
 *
 * @param RGBcolor RGB颜色字符串
 * @return cv::Scalar cv颜色
 */
cv::Scalar make_color(const str& _RGBcolor) {
	auto RGBcolor = _RGBcolor;
	int	 R, G, B;
	if (RGBcolor[0] == '#') RGBcolor = RGBcolor.substr(1);
	if (RGBcolor[0] == '(') { /* 是十进制的 */
		int retval = std::sscanf(RGBcolor.c_str(), "(%d,%d,%d)", &R, &G, &B);
		if (retval != 3)
			throw std::invalid_argument(("color not valid: " + _RGBcolor).c_str());
	}
	else { /* 是16进制的 */
		auto phrase = [&](char ch) -> int {
			if (ch >= '0' && ch <= '9') return ch - '0';
			if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
			if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
			throw std::invalid_argument(("color not valid: " + _RGBcolor).c_str());
		};
		R = phrase(RGBcolor[0]) * 16 + phrase(RGBcolor[1]);
		G = phrase(RGBcolor[2]) * 16 + phrase(RGBcolor[3]);
		B = phrase(RGBcolor[4]) * 16 + phrase(RGBcolor[5]);
	}

	return cv::Scalar(B, G, R);
}

int main(int argc, char* argv[]) {
	str color, file_name, font, output_fd, base_map, lightb, lightp, lightn, lightm,
		font_bd;
	int font_size, width, height;
	/* boost解析命令行参数 */
	namespace opt = boost::program_options;
	using opt::value, std::cout, std::endl;
	opt::options_description desc("Usage: 生成郑州地铁风格顶灯条");
	desc.add_options()														 // add paras
		("color,c", value(&color)->default_value("B12009"), "线路RGB颜色")	 // para1
		("input,i", value(&file_name)->default_value(""), "站名数据[必选]")	 // para2
		("font,f", value(&font)->default_value("resource/msyh.ttc"), "字体文件")  // para3
		("thick_font,t", value(&font_bd)->default_value("resource/msyhbd.ttc"),
		 "粗体字体文件")													  // para3.1
		("size,s", value(&font_size)->default_value(16), "字体大小")		  // para4
		("output,o", value(&output_fd)->default_value("output"), "输出路径")  // para5
		("base,b", value(&base_map)->default_value(""), "底图")				  // para6
		("height,h", value(&height)->default_value(200), "纵向大小")		  // para7
		("width,w", value(&width)->default_value(1920), "横向大小")			  // para8
		/* 建议不要改这些玩意 */
		("lightb", value(&lightb)->default_value("60E060"), "未通过车站RGB")	// para9
		("lightp", value(&lightp)->default_value("E05050"), "已通过车站RGB")	// para10
		("lightn", value(&lightn)->default_value("E0E060"), "当前车站闪灯RGB")	// para11
		("lightm", value(&lightm)->default_value("E0E060"), "车站之间闪灯RGB")	// para12
		;
	opt::variables_map vm;
	try {
		opt::store(opt::parse_command_line(argc, argv, desc), vm);
	}
	catch (...) {
		cout << "[ FAILED ]\e[1;31m ERROR: INVALID ARGS!\e[0m" << endl;
		return 1;
	}
	if (vm.count("help") || argc == 1) cout << desc;
	if (vm.count("color")) color = vm["color"].as<str>();
	if (vm.count("input")) file_name = vm["input"].as<str>();
	if (vm.count("font")) font = vm["font"].as<str>();
	if (vm.count("thick_font")) font_bd = vm["thick_font"].as<str>();
	if (vm.count("size")) font_size = vm["size"].as<int>();
	if (vm.count("output")) output_fd = vm["output"].as<str>();
	if (vm.count("base")) base_map = vm["base"].as<str>();
	if (vm.count("height")) height = vm["height"].as<int>();
	if (vm.count("width")) width = vm["width"].as<int>();
	if (vm.count("lightb")) lightb = vm["lightb"].as<str>();
	if (vm.count("lightp")) lightp = vm["lightp"].as<str>();
	if (vm.count("lightn")) lightn = vm["lightn"].as<str>();
	if (vm.count("lightm")) lightm = vm["lightm"].as<str>();
	/* 检测参数 */
	if (access(file_name.c_str(), F_OK) != 0) {
		cout << "[ FAILED ]\e[1;31m ERROR: NO CORRECT INPUT FILE FOR "
				"STATION NAME LIST!\e[0m"
			 << endl;
		return 1;
	}
	if (access("resource/main.py", F_OK) != 0) {
		cout << "[ FAILED ]\e[1;31m ERROR: FILE resource/main.py REQUIRED!\e[0m" << endl;
		return 1;
	}
	if (access(font.c_str(), F_OK) != 0) {
		cout << "[ FAILED ]\e[1;31m ERROR: NO CORRECT INPUT FILE FOR FONT "
				"FILE!\e[0m"
			 << endl;
		return 1;
	}
	if (access(font_bd.c_str(), F_OK) != 0) {
		cout << "[ FAILED ]\e[1;31m ERROR: NO CORRECT INPUT FILE FOR THICK FONT "
				"FILE!\e[0m"
			 << endl;
		return 1;
	}
	try {
		basic_metro::line_color	   = make_color(color);
		basic_metro::before_light  = make_color(lightb);
		basic_metro::passed_light  = make_color(lightp);
		basic_metro::now_st_light  = make_color(lightn);
		basic_metro::section_light = make_color(lightm);
	}
	catch (std::exception& e) {
		cout << "[ FAILED ]\e[1;31m ERROR: INPUT COLOR NOT VALID!\e[0m" << endl;
		cout << "[  INFO  ]\t" << e.what() << endl;
		return 1;
	}

	cout << "[   OK   ] selected color     = " << color << endl;
	cout << "[   OK   ] input station file = " << file_name << endl;
	cout << "[   OK   ] input font file    = " << font << endl;
	cout << "[   OK   ] word font size     = " << font_size << endl;
	cout << "[   OK   ] output folder name = " << output_fd << endl;
	cout << "[   OK   ] width size         = " << width << endl;
	cout << "[   OK   ] height size        = " << height << endl;
	cout << "[   OK   ] station not passed = " << lightb << endl;
	cout << "[   OK   ] station passed     = " << lightp << endl;
	cout << "[   OK   ] station now at     = " << lightn << endl;
	cout << "[   OK   ] light between stat = " << lightm << endl;
	if (base_map.empty())
		cout << "[   OK   ] no basemap input " << endl;
	else
		cout << "[   OK   ] basemap fpath=pos  = " << base_map << endl;

	/* check python */
	if (system("python3 --version >/dev/null") != 0) {
		cout << "[ FAILED ]\e[1;31m ERROR: DETECT python3 FAILED!\e[0m" << endl;
		return 1;
	}

	basic_metro pict(file_name, font_size, font, font_bd, output_fd);
	pict.width	  = width;
	pict.height	  = height;
	pict.base_map = base_map;
	try {
		pict.basic_gen();
		pict.image_gen();
	}
	catch (std::invalid_argument& e) { /* metroline.cpp 28 */
		cout << "[ FAILED ]\e[1;31m ERROR: INPUT BASEMAP NOT VALID!\e[0m" << endl;
		cout << "[  INFO  ]\t" << e.what() << endl;
		return 1;
	}
	catch (std::runtime_error& e) { /* metroline.hpp 46 */
		cout << "[ FAILED ]\e[1;31m ERROR: RUN PYTHON FAILED!\e[0m" << endl;
		cout << "[  INFO  ]\t" << e.what() << endl;
		return 1;
	}
	catch (std::exception& e) {
		cout << "[ FAILED ]\e[1;31m ERROR: SOME UNKNOWN ERROR!\e[0m" << endl;
		cout << "[  INFO  ]\t" << e.what() << endl;
		return 1;
	}
	cout << "[   OK   ] Photo generation finished." << endl;
	return 0;
}
